package com.allen.study.domain.service;

import cn.hutool.core.util.StrUtil;
import com.allen.study.domain.entity.EmployeeLogin;
import com.allen.study.domain.repository.IEmployeeLoginRepo;
import com.allen.study.domain.utils.security.MySysUserDetails;
import com.allen.study.domain.utils.security.SecurityUtil;
import com.allen.study.test_common.base.DomainResponse;
import com.allen.study.test_common.constant.CacheConstants;
import com.allen.study.test_common.exception.CustomRuntimeException;
import com.allen.study.test_common.utils.ServletUtils;
import com.allen.study.test_common.utils.ip.IpUtils;
import com.allen.study.test_common.utils.jwt.JwtUtils;
import com.allen.study.test_common.utils.redis.RedisUtils;
import com.allen.study.test_common.utils.uuid.IdUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotNull;
import java.util.concurrent.TimeUnit;

/**
 * 用户登录信息表领域服务
 *
 * @author AllenSun
 * @since 2025-03-06 21:43
 */
@Service
@AllArgsConstructor
@Slf4j
public class EmployeeLoginService {

    /**
     * 用户登录信息表资源库
     */
    private final IEmployeeLoginRepo employeeLoginRepo;

    @Autowired
    private AuthenticationManager authenticationManager;


    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private SecurityUtil securityUtil;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private final static long expireTime = CacheConstants.EXPIRATION;

    private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;


    /**
     * 创建用户登录信息表
     *
     * @param employeeLogin 用户登录信息表实体
     * @return 响应结果
     */
    public DomainResponse<Void> create(@NotNull EmployeeLogin employeeLogin) {

        // 对用户密码进行加密
        String password = employeeLogin.getEncryptedPassword();
        String encryptedPassword = passwordEncoder.encode(password);
        employeeLogin.setEncryptedPassword(encryptedPassword);

        // 保存用户登录信息表
        employeeLoginRepo.create(employeeLogin);
        return DomainResponse.ok();
    }

    /**
     * 根据主键删除用户登录信息表
     *
     * @param employeeLoginId 用户登录信息表主键
     * @return 响应结果
     */
    public DomainResponse<Void> deleteById(@NotNull String employeeLoginId) {
        // 根据主键查询用户登录信息表
        EmployeeLogin employeeLogin = employeeLoginRepo.queryById(employeeLoginId);
        // 用户登录信息表数据存在且租户正确，删除用户登录信息表数据
        if (ObjectUtils.isNotEmpty(employeeLogin) && employeeLogin.checkTenant()) {
            employeeLoginRepo.deleteById(employeeLoginId);
        }
        return DomainResponse.ok();
    }

    /**
     * 根据主键更新用户登录信息表
     *
     * @param employeeLogin 用户登录信息表实体
     * @return 响应结果
     */
    public DomainResponse<Void> updateById(@NotNull EmployeeLogin employeeLogin) {
        // 根据主键查询用户登录信息表
        EmployeeLogin exists = employeeLoginRepo.queryById(employeeLogin.getId());
        // 用户登录信息表数据存在且租户正确，更新用户登录信息表数据
        if (ObjectUtils.isNotEmpty(exists) && exists.checkTenant()) {
            employeeLoginRepo.updateById(employeeLogin);
        }
        return DomainResponse.ok();
    }

    /**
     * 根据主键查询用户登录信息表
     *
     * @param employeeLoginId 用户登录信息表主键
     * @return 响应结果
     */
    public DomainResponse<EmployeeLogin> queryById(@NotNull String employeeLoginId) {
        EmployeeLogin employeeLogin = employeeLoginRepo.queryById(employeeLoginId);
        return DomainResponse.ok(employeeLogin);
    }


    public DomainResponse<EmployeeLogin> queryByEmployeeId(@NotNull String employeeId) {
        EmployeeLogin employeeLogin = employeeLoginRepo.queryByEmployeeId(employeeId);
        return DomainResponse.ok(employeeLogin);
    }

    public DomainResponse<String> login(@NotNull EmployeeLogin employeeLogin) {
        Object principal = null;
        String employeeId = employeeLogin.getEmployeeId();
        if (securityUtil.isAccountLocked(employeeId)) {
            long lockLeftTime = securityUtil.getLockLeftTime(employeeId);
            String accountLockMsg = StrUtil.format("账户被锁定：{}，剩余时间：{}秒",employeeId,lockLeftTime);
            log.info(accountLockMsg);
            throw new CustomRuntimeException(accountLockMsg);
        }
        try {
            // 传入用户名和密码
            UsernamePasswordAuthenticationToken usernamePassword =
                    new UsernamePasswordAuthenticationToken(employeeId,employeeLogin.getEncryptedPassword());
            //是实现登录逻辑，此时就会去调用LoadUserByUsername方法
            Authentication authenticate = authenticationManager.authenticate(usernamePassword);
            //获取返回的用户信息
            principal = authenticate.getPrincipal();
            log.info("认证成功：{}",employeeId);
            securityUtil.clearFailureCount(employeeId);
        } catch (AuthenticationException e) {
            int leftRetryCount = securityUtil.incrementFailureCount(employeeId);
            String failLoginMsg = StrUtil.format("认证失败：{}，剩余次数：{}",employeeId,leftRetryCount);
            log.info(failLoginMsg);
            throw new CustomRuntimeException(failLoginMsg);
        }

        //强转为MySysUserDetails类型
        MySysUserDetails mySysUserDetails = (MySysUserDetails) principal;
        //输出用户信息
        log.info("用户信息：{}", mySysUserDetails);
        //返回token

        EmployeeLogin loginUser = new EmployeeLogin();
        String token = IdUtils.fastUUID();
        loginUser.setEmployeeId(mySysUserDetails.getUsername());
        loginUser.setToken(token);
        loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
        //将用户信息刷新到缓存redis中。有了token令牌
        refreshToken(loginUser);

        // Jwt存储信息（根据jwt的token可以获取uuid-token，然后从redis获取登录信息）
        mySysUserDetails.setToken(token);
        String jwtToken = jwtUtils.generateToken(mySysUserDetails);

        return DomainResponse.ok(jwtToken);
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(EmployeeLogin loginUser) {
        // 添加登录时间
        loginUser.setLoginTime(System.currentTimeMillis());
        // 添加登录过期时间
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 取出uuid（token值）作为缓存的key值
        String userKey = ACCESS_TOKEN + loginUser.getToken();
        log.info("token的缓存key生成成功：{}",userKey);
        // 把用户登录信息类loginUser作为value，uuid值作为key，存储到redis缓存中，并且设置了过期时间
        redisUtils.set(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }

}
